/*
 * Utill.cpp
 *
 *  Created on: 24-03-2013
 *      Author: Jarek
 */

#include "Util.h"
#include "Shader.h"
#include "ETexture.h"
#include <string.h>

uint 		Util::extra 		= 0;
FlatShader*	Util::flatShader 	= NULL;

uint Util::nearestPowerTwo(uint number)
{
	uint n = number > 0 ? number - 1 : 0;

	n |= n >> 1;
	n |= n >> 2;
	n |= n >> 4;
	n |= n >> 8;
	n |= n >> 16;
	n++;

	return n;
}

char* Util::readFile(char* filePath, uint* size)
{
	FILE* file = fopen(filePath, "rb");
	if(file == NULL)
	{
		LOGE("Couldn't read file: %s", filePath);
		return NULL;
	}

	fseek(file, 0, SEEK_END);
	uint fileSize = ftell(file);
	rewind(file);

	if(size != NULL)
		*size = fileSize;

	char* buffer = new char[fileSize+1];
	fread(buffer, 1, fileSize, file);
	fclose(file);

	buffer[fileSize] = '\0';

	return buffer;
}

char* Util::getFileName(const char* filePath)
{
	const char* lastBackSlash	= strrchr(filePath, '\\');
	const char* lastSlash		= strrchr(filePath, '/');

	const char* fileNameBegin	= lastBackSlash > lastSlash ? lastBackSlash : lastSlash;
	if(fileNameBegin != NULL && fileNameBegin[0] != '\0')
	{
		fileNameBegin++;
		char *name = new char[strlen(fileNameBegin) + 1];
		strcpy(name, fileNameBegin);
		return name;
	}
}

char* Util::getPath(const char* fileDirectory, const char* fileName)
{
	uint dirLength		= strlen(fileDirectory);
	uint fileLenght		= strlen(fileName);

	int addSlash = 0;
	if(fileDirectory[dirLength-1] != '\\' && fileDirectory[dirLength-1] != '/')
		addSlash	= 1;

	char* path = new char[dirLength + fileLenght + 1 + addSlash];
	strcpy(path, fileDirectory);
	if(addSlash == 1)
	{
		path[dirLength-1]	= '/';
		path[dirLength]		= '\0';
	}
	strcat(path, fileName);
	return path;

}

void Util::initFlatShader(char* VSpath, char* FSPath)
{
	extra 		|= UTIL_EXTRA_LOAD_FLAT_SHADER;
	flatShader = new FlatShader(VSpath, FSPath);
}

void Util::onDestroy()
{
	DELETE(flatShader);
}

void Util::drawQuad(const Texture& texture, GLint indicesHandle, GLint verticesHandle, GLint texcoordsHandle, Matrix4x4<float> translationMatrix)
{
	if(!(extra && UTIL_EXTRA_LOAD_FLAT_SHADER))
	{
		if(texture.name != NULL && texture.name[0] != '\0')
			LOGE("Engine2d::drawQuad() for texture %s failed: Flat shader not created.", texture.name);
		else
			LOGE("Engine2d::drawQuad() for unnamed texture failed: Flat shader not created.");
		return;
	}

		CameraLogic camera;
		camera.setOrtho(0, deviceInfo.getScreenWidth(), 0, deviceInfo.getScreenHeight());
		camera.projectionMatrix *= translationMatrix;
		flatShader->draw(camera.projectionMatrix, indicesHandle, verticesHandle, texcoordsHandle, texture, 6);
}

void Util::drawQuad(const Texture& texture, int x, int y, uint width, uint height)
{
	if (!(extra & UTIL_EXTRA_LOAD_FLAT_SHADER))
	{
		if (texture.name != NULL && texture.name[0] != '\0')
			LOGE("Engine2d::drawQuad() for texture %s failed: Flat shader not created.", texture.name);
		else
			LOGE("Engine2d::drawQuad() for unnamed texture failed: Flat shader not created.");
		return;
	}

	GLfloat pos[] = {
		x, 			y, 			0,
		x + width, 	y, 			0,
		x, 			y + height, 0,
		x + width, 	y + height, 0
	};

	float scaledX = static_cast<float>(texture.imageWidth)  / static_cast<float>(texture.textureWidth);
	float scaledY = static_cast<float>(texture.imageHeight) / static_cast<float>(texture.textureHeight);

	GLfloat coords[] = {
		0.0f, 0.0f,
		scaledX, 0.0f,
		0.0f, scaledY,
		scaledX, scaledY
	};

	GLuint indices[] = {
		0, 1, 2, 2, 1, 3
	};

	CameraLogic camera;
	camera.setOrtho(0, deviceInfo.getScreenWidth(), 0, deviceInfo.getScreenHeight());
	GLuint indicesHandle, verticesHandle, texcoordsHandle;

	glGenBuffers(1, &indicesHandle);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesHandle);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint) * 6, indices, GL_STREAM_DRAW);

	glGenBuffers(1, &verticesHandle);
	glBindBuffer(GL_ARRAY_BUFFER, verticesHandle);
	glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, pos, GL_STREAM_DRAW);

	glGenBuffers(1, &texcoordsHandle);
	glBindBuffer(GL_ARRAY_BUFFER, texcoordsHandle);
	glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8, coords, GL_STREAM_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

	flatShader->draw(camera.projectionMatrix, indicesHandle, verticesHandle, texcoordsHandle, texture, 6);

	glDeleteBuffers(1, &indicesHandle);
	glDeleteBuffers(1, &verticesHandle);
	glDeleteBuffers(1, &texcoordsHandle);


}

/**
 * Converts string stored in char array encoded in UTF to wchar_t.
 * @param dest Pointer to the buffer where the result should be stored.
 * @param src Source char array encoded in UTF8.
 */
void convertUTF8StringToWchar(wchar_t *dest, const char *src) {
    wchar_t *destptr = dest;
    char *srcptr = const_cast<char*>(src);

    while(*srcptr != '\0') {
        wchar_t value = 0;
        unsigned char mask;
        // Get number of bytes that character takes
        char first = *srcptr;
        char count = 0;
        int firstByteUnusedBits;
        for(int i=0; i<8; i++) {
            if(0x80&first) { // 0x80 == 0b10000000
                first = first<<1;
                count++;
            } else {
                break;
            }
        }

        firstByteUnusedBits = count + 1;

        // Copy info from first byte
        mask = ~0;
        mask = (mask >> firstByteUnusedBits);
        value = (*srcptr) & mask;
        srcptr++;
        if(count > 0) {
            count--;
            mask = 0x3F; // 0x3f = 0b00111111
        }

        // Copy info from other bytes (if any)
        while(count > 0) {
            value = value << 6;
            value = value | ((*srcptr) & mask);
            srcptr++;
            count--;
        }

        // Copy value to destination and increment pointer
        *destptr = value;
        destptr++;
    }
    *destptr = L'\0';
}

/**
 * Converts character stored in char array encoded in UTF to wchar_t.
 * @param src Source char array encoded in UTF8.
 * @param bytes Pointer to store number of bytes on which the character was encoded.
 * @return wchar_t value of read character.
 */
wchar_t convertUTF8CharToWchar(const char *src, uint *bytes = NULL) {
    char *srcptr = const_cast<char*>(src);

    wchar_t value = 0;
    unsigned char mask;
    // Get number of bytes that character takes
    char first = *srcptr;
    char count = 0;
    int firstByteUnusedBits;
    for(int i=0; i<8; i++) {
        if(0x80&first) { // 0x80 == 0b10000000
            first = first<<1;
            count++;
        } else {
            break;
        }
    }

    firstByteUnusedBits = count + 1;

    // Copy info from first byte
    mask = ~0;
    mask = (mask >> firstByteUnusedBits);
    value = (*srcptr) & mask;
    srcptr++;
    if(count > 0) {
        count--;
        mask = 0x3F; // 0x3f = 0b00111111
    }

    // Copy info from other bytes (if any)
    while(count > 0) {
        value = value << 6;
        value = value | ((*srcptr) & mask);
        srcptr++;
        count--;
    }

    // Copy value to destination and increment pointer
    if (bytes != NULL)
        *bytes = static_cast<uint>(srcptr - src);
    return value;
}

/**
 * Returns wchar_t unicode value from given string and increments the string pointer.
 */
wchar_t getNextWCharFromUTF8(char *(&src)) {
    wchar_t value = 0;
    unsigned char mask;
    // Get number of bytes that character takes
    char first = *src;
    char count = 0;
    int firstByteUnusedBits;
    for(int i=0; i<8; i++) {
        if(0x80&first) { // 0x80 == 0b10000000
            first = first<<1;
            count++;
        } else {
            break;
        }
    }

    firstByteUnusedBits = count + 1;

    // Copy info from first byte
    mask = ~0;
    mask = (mask >> firstByteUnusedBits);
    value = (*src) & mask;
    src++;
    if(count > 0) {
        count--;
        mask = 0x3F; // 0x3f = 0b00111111
    }

    // Copy info from other bytes (if any)
    while(count > 0) {
        value = value << 6;
        value = value | ((*src) & mask);
        src++;
        count--;
    }

    return value;
}

/**
 * Counts UTF8 string lenght.
 */
size_t UTF8StringLength(const char *src) {
    size_t length = 0;
    char *srcptr = const_cast<char*>(src);
    while(*srcptr != '\0') {
        length++;
        unsigned char mask;
        // Get number of bytes that character takes
        char first = *srcptr;
        char count = 0;
        int firstByteUnusedBits;
        for(int i=0; i<8; i++) {
            if(0x80&first) { // 0x80 == 0b10000000
                first = first<<1;
                count++;
            } else {
                srcptr += count==0 ? 1 : count;
                break;
            }
        }
    }
    return length;
}


